package com.opencee.cloud.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.opencee.boot.db.mybatis.service.impl.SupperServiceImpl;
import com.opencee.cloud.base.constants.BaseConstants;
import com.opencee.cloud.base.constants.BaseGrantToType;
import com.opencee.cloud.base.constants.BaseMenuType;
import com.opencee.cloud.base.constants.BaseResourceType;
import com.opencee.cloud.base.entity.BaseMenuEntity;
import com.opencee.cloud.base.mapper.BaseMenuMapper;
import com.opencee.cloud.base.service.IBaseMenuService;
import com.opencee.cloud.base.service.IBasePrivilegesService;
import com.opencee.cloud.base.vo.BaseMenuVO;
import com.opencee.cloud.base.vo.params.BaseGrantParams;
import com.opencee.common.exception.BaseFailException;
import com.opencee.common.utils.RedisTemplateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 功能菜单
 * </p>
 *
 * @author liuyadu
 * @since 2021-04-16
 */
@Service
public class BaseMenuServiceImpl extends SupperServiceImpl<BaseMenuMapper, BaseMenuEntity> implements IBaseMenuService {

    @Autowired
    private IBasePrivilegesService privilegesService;

    @Autowired
    private RedisTemplateUtil redisTemplateUtil;


    /**
     * 检测是否存在
     *
     * @param code
     * @param type
     * @return
     */
    @Override
    public boolean exists(String code, String type) {
        QueryWrapper<BaseMenuEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(BaseMenuEntity::getCode, code)
                .eq(BaseMenuEntity::getType, type);
        return super.count(wrapper) > 0;
    }

    /**
     * 获取
     *
     * @param code
     * @param type
     * @return
     */
    @Override
    public BaseMenuEntity getBy(String code, String type) {
        QueryWrapper<BaseMenuEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(BaseMenuEntity::getCode, code)
                .eq(BaseMenuEntity::getType, type);
        return super.getOne(wrapper);
    }

    /**
     * 添加
     *
     * @param menu
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(BaseMenuVO menu) {
        Assert.hasText(menu.getName(), "名称不能为空");
        Assert.hasText(menu.getCode(), "编码不能为空");
        Assert.hasText(menu.getType(), "类型不能为空");
        Assert.hasText(menu.getSid(), "系统ID不能为空");
        Assert.notNull(BaseMenuType.getByValue(menu.getType()), "类型不支持:" + menu.getType());
        Assert.hasText(menu.getAuthority(), "权限名称不能为空");
        String prefix = menu.getType() + BaseConstants.SEPARATOR;
        Assert.isTrue(menu.getAuthority().startsWith(prefix), "权限名称必须以[" + prefix + "]开头");
        if (exists(menu.getCode(), menu.getType())) {
            throw new BaseFailException(String.format("编码(%s)已存在!", menu.getCode()));
        }
        if (menu.getSort() == null) {
            menu.setSort(0);
        }
        if (menu.getStatus() == null) {
            menu.setStatus(1);
        }
        BaseMenuEntity entity = new BaseMenuEntity();
        entity.setName(menu.getName());
        entity.setCode(menu.getCode());
        entity.setType(menu.getType());
        entity.setSid(menu.getSid());
        entity.setPath(menu.getPath());
        entity.setParentId(menu.getParentId());
        entity.setMetaInfo(menu.getMeta().toJSONString());
        entity.setAuthority(menu.getAuthority());
        entity.setRemark(menu.getRemark());
        entity.setSort(menu.getSort());
        entity.setStatus(menu.getStatus());
        boolean flag = super.save(entity);
        menu.setId(entity.getId());
        // 更新缓存
        putCache(entity);
        // 操作项关联接口
        if (BaseMenuType.BTN.equals(entity.getType()) && !CollectionUtils.isEmpty(menu.getApiIds())) {
            BaseGrantParams grantParams = new BaseGrantParams();
            grantParams.setGrantResIds(menu.getApiIds());
            grantParams.setGrantToId(entity.getId());
            privilegesService.grantSave(BaseGrantToType.BTN, BaseResourceType.API, grantParams);
        }
        return flag;
    }

    /**
     * 更新
     *
     * @param menu
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(BaseMenuVO menu) {
        Assert.hasText(menu.getName(), "名称不能为空");
        Assert.hasText(menu.getCode(), "编码不能为空");
        Assert.hasText(menu.getType(), "类型不能为空");
        Assert.hasText(menu.getSid(), "系统ID不能为空");
        Assert.notNull(BaseMenuType.getByValue(menu.getType()), "类型不支持:" + menu.getType());
        Assert.hasText(menu.getAuthority(), "权限名称不能为空");
        String prefix = menu.getType() + BaseConstants.SEPARATOR;
        Assert.isTrue(menu.getAuthority().startsWith(prefix), "权限名称必须以[" + prefix + "]开头");
        if (menu.getSort() == null) {
            menu.setSort(0);
        }
        if (menu.getStatus() == null) {
            menu.setStatus(1);
        }
        BaseMenuEntity saved = getById(menu.getId());
        if (saved == null) {
            if (!saved.getCode().equals(menu.getCode()) || !saved.getType().equals(menu.getType())) {
                // 和原来不一致重新检查唯一性
                if (exists(menu.getCode(), menu.getType())) {
                    throw new BaseFailException(String.format("编码(%s)已存在!", menu.getCode()));
                }
            }
        }
        saved.setName(menu.getName());
        saved.setCode(menu.getCode());
        saved.setType(menu.getType());
        saved.setPath(menu.getPath());
        saved.setParentId(menu.getParentId());
        saved.setSid(menu.getSid());
        saved.setMetaInfo(menu.getMeta().toJSONString());
        saved.setAuthority(menu.getAuthority());
        saved.setRemark(menu.getRemark());
        saved.setSort(menu.getSort());
        saved.setStatus(menu.getStatus());
        saved.setUpdateTime(new Date());
        boolean flag = updateById(saved);
        // 级联更新状态
        updateChildrenStatus(saved.getId(), saved.getStatus());
        menu.setId(saved.getId());
        // 更新缓存
        putCache(saved);
        // 操作项关联接口
        if (BaseMenuType.BTN.equals(saved.getType()) && !CollectionUtils.isEmpty(menu.getApiIds())) {
            BaseGrantParams grantParams = new BaseGrantParams();
            grantParams.setGrantResIds(menu.getApiIds());
            grantParams.setGrantToId(saved.getId());
            privilegesService.grantSave(BaseGrantToType.BTN, BaseResourceType.API, grantParams);
        }
        return flag;
    }

    private void putCache(BaseMenuEntity entity) {
        if (entity != null && StringUtils.isNotBlank(entity.getPath())) {
            String key = entity.getPath() + BaseConstants.SEPARATOR + entity.getSid();
            redisTemplateUtil.hset(BaseConstants.CACHE_MENU_MAP_KEY, key, entity);
        }
    }

    private void removeCache(BaseMenuEntity entity) {
        if (entity != null && StringUtils.isNotBlank(entity.getPath())) {
            String key = entity.getPath() + BaseConstants.SEPARATOR + entity.getSid();
            redisTemplateUtil.hdel(BaseConstants.CACHE_MENU_MAP_KEY, key);
        }
    }

    /**
     * 更新子级状态
     *
     * @param parentId
     * @param status
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateChildrenStatus(Long parentId, Integer status) {
        if (status != 0) {
            return false;
        }
        QueryWrapper<BaseMenuEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(BaseMenuEntity::getParentId, parentId);
        BaseMenuEntity entity = new BaseMenuEntity();
        entity.setStatus(status);
        return update(entity, wrapper);
    }

    /**
     * 批量删除
     *
     * @param sid
     * @param types
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeBy(String sid, Set<String> types) {
        QueryWrapper<BaseMenuEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(BaseMenuEntity::getSid, sid)
                .in(BaseMenuEntity::getType, types);
        Set<Long> ids = list(wrapper).stream().map(t -> t.getId()).collect(Collectors.toSet());
        boolean flag = remove(wrapper);
        if (flag) {
            // 级联删除授权
            privilegesService.removeByGrantIds(BaseResourceType.MENU, ids);
        }
        return flag;
    }

    /**
     * 查询列表
     *
     * @param status
     * @return
     */
    @Override
    public List<BaseMenuVO> list(String sid, Integer status) {
        Map<String, Object> map = new HashMap<>(8);
        map.put("status", status);
        map.put("sid", sid);
        return baseMapper.selectMenuVo(map);
    }

    /**
     * 根据父节点删除
     *
     * @param parentId
     * @return
     */
    @Override
    public Boolean removeByParentId(Long parentId) {
        QueryWrapper<BaseMenuEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(BaseMenuEntity::getParentId, parentId);
        boolean flag = remove(wrapper);
        return flag;
    }


    @Override
    public boolean removeById(Serializable id) {
        BaseMenuEntity entity = getById(id);
        // 移除缓存
        removeCache(entity);
        boolean flag = super.removeById(id);
        if (flag) {
            // 级联删除
            this.removeByParentId((Long) id);
            privilegesService.removeByGrantIds(BaseResourceType.MENU, new HashSet(Arrays.asList(id)));
        }
        return flag;
    }

    @Override
    public boolean removeByIds(Collection<?> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            return false;
        }
        idList.forEach(t -> {
            BaseMenuEntity entity = getById((Serializable) t);
            // 移除缓存
            removeCache(entity);
        });
        boolean flag = super.removeByIds(idList);
        if (flag) {
            // 级联删除
            QueryWrapper<BaseMenuEntity> wrapper = new QueryWrapper<>();
            wrapper.lambda().in(BaseMenuEntity::getParentId, idList);
            privilegesService.removeByGrantIds(BaseResourceType.MENU, new HashSet(idList));
        }
        return flag;
    }


    @Override
    public boolean saveOrUpdateBatch(Collection<BaseMenuEntity> entityList) {
        boolean flag = super.saveOrUpdateBatch(entityList);
        entityList.forEach(t -> {
            putCache(t);
        });
        return flag;
    }
}
